package com.liyunc.demo.detect.service;

import com.liyunc.demo.detect.recognition.PredictRlt;
import com.liyunc.demo.detect.utils.Constants;
import lombok.extern.slf4j.Slf4j;
import org.bytedeco.javacpp.opencv_core;
import org.bytedeco.javacpp.opencv_face;

import static org.bytedeco.javacpp.opencv_imgproc.resize;

/**
 * 人脸认证服务.
 */
@Slf4j
public class RecognizeService {

    private final opencv_face.FaceRecognizer faceRecognizer;

    // 推理结果的标签
    private final int[] labels;

    // 推理结果的置信度
    private final double[] confidences;

    // 推理结果
    private final PredictRlt predictRlt;

    // 用于推理的图片尺寸，要和训练时的尺寸保持一致
    private final opencv_core.Size size = new opencv_core.Size(
            Constants.RESIZE_WIDTH, Constants.RESIZE_HEIGHT);

    /**
     * 构造函数.
     *
     * @param modelPath 训练生成的模型文件
     */
    public RecognizeService(String modelPath) {
        labels = new int[1];
        confidences = new double[1];
        predictRlt = new PredictRlt();

        // 识别类的实例化，与训练时相同
        faceRecognizer = opencv_face.FisherFaceRecognizer.create();
        // 加载的是训练生成的模型
        faceRecognizer.read(modelPath);
        // 设置门限，这个可以根据您自身的情况不断调整
        faceRecognizer.setThreshold(Constants.MAX_CONFIDENCE);
    }

    /**
     * 将Mat实例给模型去推理.
     *
     * @param mat 需要推理的图片
     */
    public PredictRlt predict(opencv_core.Mat mat) {
        // 调整到和训练一致的尺寸
        resize(mat, mat, size);

        boolean isFinish = false;

        try {
            log.info("face recognizer start");
            // 推理
            // 实测发现，在一张照片中出现多个人脸时，faceRecognizer.predict可能抛出
            // RuntimeException异常，因此这里要捕获异常，避免程序崩溃退出
            faceRecognizer.predict(mat, labels, confidences);
            isFinish = true;
        } catch (RuntimeException runtimeException) {
            log.error("face recognizer fail", runtimeException);
        }

        // 如果发生过异常，就提前返回
        if (!isFinish) {
            return null;
        }

        // 将推理结果写入返回对象中
        predictRlt.setLabel(labels[0]);
        predictRlt.setConfidence(confidences[0]);

        return predictRlt;
    }
}
